Introduction

Part of the Accelerator Readiness Review is looking at muons that leave the ring. To this end, we (Renee, Nathan, James, and myself) have a preliminary version of MDC-2 with full injection, unified fields, and “ghost” detectors in a Geant parallel world (so these sensitive volumes can be placed without overlapping physical structures). We can remove various magnets and fields and see how that affects muons escaping the beam.

# Load libraries
library(readGallery)
library(stringr)
library(dplyr)
library(ggplot2)
library(rgl)

Samples

I have run preliminary MDC-2 (using Renee’s FCL files), making 10K events for various scenarios. Files are currently stored in /pnfs/GM2/scratch/users/lyon/arr_20170313 and directories within. Ghost detectors included are Renee’s cylinder just at the outer vacuum wall and encompossing the inflector as well as my ghost detector that is a cylindrical shell just on the inside of the world cube. Note that this code includes the magnet yoke steel.

Find the data

# Let's look at only the 180* job series (so we just get 10,000 events per scenario)
system('ifdh ls /pnfs/GM2/scratch/users/lyon/arr_20170313/180*/*.root | grep .root', intern=T) %>% 
  xrootify() -> arrFiles
arrFiles
[1] "root://fndca1.fnal.gov/pnfs/fnal.gov/usr/GM2/scratch/users/lyon/arr_20170313/18089099_0/ARR_unified_noKickerQuads_cyl.root"
[2] "root://fndca1.fnal.gov/pnfs/fnal.gov/usr/GM2/scratch/users/lyon/arr_20170313/18089123_0/ARR_unified_noKicker_cyl.root"     
[3] "root://fndca1.fnal.gov/pnfs/fnal.gov/usr/GM2/scratch/users/lyon/arr_20170313/18089132_0/ARR_unified_noQuads_cyl.root"      
[4] "root://fndca1.fnal.gov/pnfs/fnal.gov/usr/GM2/scratch/users/lyon/arr_20170313/18089097_0/ARR_unified_noInflector_cyl.root"  
[5] "root://fndca1.fnal.gov/pnfs/fnal.gov/usr/GM2/scratch/users/lyon/arr_20170313/18089092_0/ARR_unified_everything_cyl.root"   
[6] "root://fndca1.fnal.gov/pnfs/fnal.gov/usr/GM2/scratch/users/lyon/arr_20170313/18089095_0/ARR_unified_noDipole_cyl.root"     

Let’s pluck out the scenario.

scenarios <- str_match(arrFiles, 'arr_20170313/.+_unified_(.+)_cyl')[,2]
scenarios
[1] "noKickerQuads" "noKicker"      "noQuads"       "noInflector"   "everything"    "noDipole"     

What data are in these file? Well, product_sizes_dumper doesn’t seem to work across XRootD. That’s too bad.

Here’s a picture from Root.

Look at Tracking action data to see how mnay muons decay and how many escape

useDataProduct('std::vector<gm2truth::TrackingActionArtRecord>')

Create a reader class (modify outside of this notebook) for TrackingActionArtRecord

readerClassSkel('gm2truth::TrackingActionArtRecord', writeFile = 'trackingActionReader.py')

trackingActionReader

For documentation, here is the TrackingActionArtRecordReader reader class

readr::read_file('trackingActionReader.py') %>% cat
from readGallery import GalleryReaderBase  # Necessary for the base class

class TrackingActionArtRecordReader(GalleryReaderBase):
  def __init__(self, inputTag, evLimit):
    GalleryReaderBase.__init__(self, inputTag)
    self.names = ['fileEntry', 'eventEntry', 'trackType', 'trackID', 
                  'parentTrackID', 'volumeUID', 'status', 'x', 'y', 'z', 'px', 'py', 'pz']
    self.evLimit = evLimit

  def prepare(self, ROOT, ev):
    GalleryReaderBase.prepare(self, ROOT, ev)
    self.getValidHandle = ev.getValidHandle(ROOT.vector(ROOT.gm2truth.TrackingActionArtRecord))

  def fill(self, ROOT, ev):
    
    if ev.eventEntry() > self.evLimit:
      return False
    
    validHandle = self.getValidHandle(self.inputTag)  # Get the valid handle for gm2truth::TrackingActionArtRecord

    if not validHandle.empty():                       # Does it have data?

      p = validHandle.product()                       # Get the corresponding data product (maybe a vector)

      # Fill from gm2truth::TrackingActionArtRecord
      for e in p:                             # Loop over elements and fill
      
        # Let's only accept the muon
        if e.trackID != 1 or e.parentTrackID != 0:
          continue

        self.vals.append(
          [ ev.fileEntry(), ev.eventEntry(), e.trackType, e.trackID, e.parentTrackID,  e.volumeUID, 
            e.status, e.x, e.y, e.z, e.px, e.py, e.pz ])

    return True

Make an instance

trackingActionReaderClass <- createReaderClass_from_file('trackingActionReader.py')$TrackingActionArtRecordReader

Load the data - let’s just look at one file since it takes a long time

taReader <- trackingActionReaderClass(artInputTag("artg4"), 20000)
getGalleryData(arrFiles[1], taReader)
Opening first file...
   'cannot obtain credentials for protocol: Secgsi: ErrParseBuffer: error getting user proxies: kXGS_init: unable to get protocol object.'
Error in py_call_impl(callable, dots$args, dots$keywords) : 
  Exception: gallery::Event::gallery::Event(const vector<string>& fileNames, bool useTTreeCache = true, unsigned int eventsToLearnUsedBranches = 7) =>
    ---- FileOpenError BEGIN
  Failed opening file 'root://fndca1.fnal.gov/pnfs/fnal.gov/usr/GM2/scratch/users/lyon/arr_20170313/18089099_0/ARR_unified_noKickerQuads_cyl.root'
---- FileOpenError END
 (C++ exception of type cet::coded_exception<art::errors::ErrorCodes,&art::ExceptionDetail::translate>)

Detailed traceback: 
  File "/home/me/R/x86_64-redhat-linux-gnu-library/3.3/readGallery/python/readGallery.py", line 88, in getGalleryData
    ev = ROOT.gallery.Event(fileVector)

As of right now, the dCache XRootD door is done.

nrow(taDF)
[1] 120000

Set the scenario (we’ll need that again, so make it a function)

setScenario <- function(df) {
  df %>% mutate(scenario = factor(fileEntry, levels=0:(length(scenarios)-1), labels=scenarios))
}
taDF %>% setScenario() %>% select(scenario, everything()) -> taDF
taDF

How many track action hits do we get per scenario.

taDF %>% group_by(scenario) %>% tally()

Let’s just get the death of the muons.

Muon deaths in tracking action

taDF %>% filter(status == 1) -> taDeathDF

We need the volume ID. I have a art FCL to run to get this information. I have files and they each may have a different set of volume IDs. Let’s try to do this in parallel. The examples in the help for parallel::mcparallel seem to be useful here.

Create the command strings,

runForVolIDString <- function(i) {
  str_interp(
    "PVS_CSVOUT=${csvout}_volNames.csv gm2 -c ${fclPath}/gm2analyses/fcl/physicalVolumeStoreToFile.fcl -n 1 ${aFile}",
    list( csvout=scenarios[i], fclPath=Sys.getenv("MRB_BUILDDIR"), aFile=arrFiles[i]) 
  )
}
runForVolIDStrings <- sapply(1:length(arrFiles), runForVolIDString)
runForVolIDStrings
[1] "PVS_CSVOUT=noKickerQuads_volNames.csv gm2 -c /home/me/gm2/renee_arr/build_slf7.x86_64/gm2analyses/fcl/physicalVolumeStoreToFile.fcl -n 1 root://fndca1.fnal.gov/pnfs/fnal.gov/usr/GM2/scratch/users/lyon/arr_20170313/18089099_0/ARR_unified_noKickerQuads_cyl.root"
[2] "PVS_CSVOUT=noKicker_volNames.csv gm2 -c /home/me/gm2/renee_arr/build_slf7.x86_64/gm2analyses/fcl/physicalVolumeStoreToFile.fcl -n 1 root://fndca1.fnal.gov/pnfs/fnal.gov/usr/GM2/scratch/users/lyon/arr_20170313/18089123_0/ARR_unified_noKicker_cyl.root"          
[3] "PVS_CSVOUT=noQuads_volNames.csv gm2 -c /home/me/gm2/renee_arr/build_slf7.x86_64/gm2analyses/fcl/physicalVolumeStoreToFile.fcl -n 1 root://fndca1.fnal.gov/pnfs/fnal.gov/usr/GM2/scratch/users/lyon/arr_20170313/18089132_0/ARR_unified_noQuads_cyl.root"            
[4] "PVS_CSVOUT=noInflector_volNames.csv gm2 -c /home/me/gm2/renee_arr/build_slf7.x86_64/gm2analyses/fcl/physicalVolumeStoreToFile.fcl -n 1 root://fndca1.fnal.gov/pnfs/fnal.gov/usr/GM2/scratch/users/lyon/arr_20170313/18089097_0/ARR_unified_noInflector_cyl.root"    
[5] "PVS_CSVOUT=everything_volNames.csv gm2 -c /home/me/gm2/renee_arr/build_slf7.x86_64/gm2analyses/fcl/physicalVolumeStoreToFile.fcl -n 1 root://fndca1.fnal.gov/pnfs/fnal.gov/usr/GM2/scratch/users/lyon/arr_20170313/18089092_0/ARR_unified_everything_cyl.root"      
[6] "PVS_CSVOUT=noDipole_volNames.csv gm2 -c /home/me/gm2/renee_arr/build_slf7.x86_64/gm2analyses/fcl/physicalVolumeStoreToFile.fcl -n 1 root://fndca1.fnal.gov/pnfs/fnal.gov/usr/GM2/scratch/users/lyon/arr_20170313/18089095_0/ARR_unified_noDipole_cyl.root"          

Let’s run in parallel

jobs <- lapply(1:length(arrFiles), function(i) mcparallel(system( runForVolIDStrings[i], intern=T ), name=i))
res <- mccollect(jobs)

Now we need to load the csv files. Let’s do that in parallel too

jobs <- lapply(1:length(arrFiles), function(i) paste0(scenarios[i], "_volNames.csv") %>% read_csv(col_names=c("volumeUID", "volName")) %>% mcparallel(name=i) )
volNameTables <- mccollect(jobs)
names(volNameTables) <- scenarios

Let’s see what we got

volNameTables[["everything"]]

Join them together when the scenario name.

volNameTable <- map2_df(volNameTables, scenarios, function(df, sce) df %>% mutate(scenario = sce) )
volNameTable

Now let’s do lookups… [Note that I can’t do what I originally thought, which was split up the data frame into groups by scenario and then put in the factor by group – because in the end the rows get rejoined and then row-binded factor gets messed up.]

taDeathDF -> hold # in case we mess up
taDeathDF %>% inner_join(volNameTable) %>% select(scenario, eventEntry, volName, everything()) -> taDeathDF
Joining, by = c("scenario", "volumeUID")
joining factor and character vector, coercing into character vector
taDeathDF

We can check this – we should see different volume names associated with volume IDs for different scenarios

taDeathDF %>% distinct(scenario, volumeUID, volName) %>% arrange(volumeUID)

Let’s plot where things die

taDeathDF %>% group_by(scenario, volName) %>% tally() %>% arrange(scenario, desc(n)) -> deathVolumeTable
deathVolumeTable

Did any of them actually decay?

taDeathDF %>% filter(volName == "ArcSection[00]")

Uh oh!!!! Hardly any decay!

taDeathDF %>% ggplot( aes(x = volName) ) + geom_bar() + theme(axis.text.x = element_text(angle=90, hjust=1))

Look at why hardly any muons decay.

Let’s look at the ‘everything’ scenario.

everythingFile <- which(scenarios == "everything")

I made a new TrackingAction reader

readr::read_file('trackingActionReaderCheckDecay.py') %>% cat
# Store the muon at death and whatever it decayed to at birth

from readGallery import GalleryReaderBase  # Necessary for the base class

class TrackingActionWithDecayReader(GalleryReaderBase):
  def __init__(self, inputTag, evLimit):
    GalleryReaderBase.__init__(self, inputTag)
    self.names = ['fileEntry', 'eventEntry', 'trackType', 'trackID', 
                  'parentTrackID', 'volumeUID', 'status', 'x', 'y', 'z', 'px', 'py', 'pz', 'e', 'turn']
    self.evLimit = evLimit

  def prepare(self, ROOT, ev):
    GalleryReaderBase.prepare(self, ROOT, ev)
    self.getValidHandle = ev.getValidHandle(ROOT.vector(ROOT.gm2truth.TrackingActionArtRecord))

  def fill(self, ROOT, ev):
    
    if ev.eventEntry() > self.evLimit:
      return False
    
    validHandle = self.getValidHandle(self.inputTag)  # Get the valid handle for gm2truth::TrackingActionArtRecord

    if not validHandle.empty():                       # Does it have data?

      p = validHandle.product()                       # Get the corresponding data product (maybe a vector)

      # Fill from gm2truth::TrackingActionArtRecord
      for e in p:                             # Loop over elements and fill
      
        # Let's only accept the muon at death or whatever the muon decays to at birth
        if (e.trackID == 1) or (e.parentTrackID == 1 and e.status == 0):

          self.vals.append(
            [ ev.fileEntry(), ev.eventEntry(), e.trackType, e.trackID, e.parentTrackID,  e.volumeUID, 
              e.status, e.x, e.y, e.z, e.px, e.py, e.pz, e.e, e.turn ])

    return True

Make an instance

taDecayClass <- createReaderClass_from_file('trackingActionReaderCheckDecay.py')$TrackingActionWithDecayReader

Load the data - let’s just look at one file since it takes a long time

taReader <- taDecayClass(artInputTag("artg4"), 20000)
getGalleryData(arrFiles[5], taReader)
Opening first file...
Successfully opened file root://fndca1.fnal.gov/pnfs/fnal.gov/usr/GM2/scratch/users/lyon/arr_20170313/18089092_0/ARR_unified_everything_cyl.root
tadDF <- galleryReader_df(taReader)
nrow(tadDF)
[1] 52842

Make things easier to look at

tadDF %>% mutate(r = sqrt(x*x+y*y)) %>% select(trackType, volumeUID, status, r, e, turn) -> tadDF

So I see an electron in alomst every case. But the muon appears to stop in the iron. Let’s just plot x,z

taDeathDF %>% ggplot(aes(x=z, y=x)) + 
  geom_point() + xlim(c(-10000, 10000))

tmp <- open3d()
ring <- cylinder3d( center=rbind(c(0,-90, 0), c(0,90,0)),
                    radius=7112,
                    sides=20, closed = F)
clear3d()
with(taDeathDF, 
     plot3d(x=x, y=y, z=z, type='p', main='Muon death positions', 
       xlab="x", ylab="y", zlab="z", xlim=c(-9000, 9000), zlim=c(-9000, 9000)))
plot3d(ring, add=T, alpha=0.2)
view3d(phi=90, theta=-90)
rglwidget()

This really doesn’t look like muons are being stored.

Look at Renee’s cylinder hits (this is the cylinder just in the outer wall of the vacuum chamber and around the inflector).

useDataProduct('std::vector<gm2truth::GhostDetectorArtRecord>')
ghostCylinderInputTag = artInputTag('artg4:GhostCylinderDetector')

Create a reader class (modify outside of this notebook) for GhostDetectorArtRecord

readerClassSkel('gm2truth::GhostDetectorArtRecord', writeFile = 'ghostDetectorReader.py')

Here’s the reader class

readr::read_file('ghostDetectorReader.py') %>% cat

Make an instance

ghostDetectorReaderClass <- createReaderClass_from_file('ghostDetectorReader.py')$GhostDetectorArtRecordReader
ghostDetectorReader = ghostDetectorReaderClass(ghostCylinderInputTag)

Load the data

getGalleryData(arrFiles, ghostDetectorReader)
gcDF <- galleryReader_df(ghostDetectorReader)

How many events did we get?

nrow(gcDF)
head(gcDF)

Make factors…

Some common particles are

# See  http://pdg.lbl.gov/2007/reviews/montecarlorpp.pdf for PDG value meanings
pdgs <- c('e-'= 11, 'nu_e'= 12, 'mu-'= 13, 'nu_mu'= 14, 'tau-'= 15, 'nu_tau'= 16,
          'e+'=-11, 'anti_nu_e'=-12, 'mu+'=-13, 'anti_nu_mu'=-14, 'tau+'=-15, 'anti_nu_tau'=-16,
          'gam'=22, 'p'=2212, 'n'=2112, 'anti-p'=-2212, 'anti-n'=-2112) 
pdgs
gcDF %>% mutate(scenario = factor(fileEntry, levels=0:(length(scenarios)-1), labels=scenarios),
                pdg = factor(particleID, levels=pdgs, labels=names(pdgs))) %>% 
  select(scenario, eventEntry, pdg, everything())-> gcDF
gcDF

How many lost per scenario

gcDF %>% filter(pdg == 'mu+') %>% group_by(scenario) %>% tally()

How many events?

gcDF %>% group_by(scenario) %>% tally()

See evidence of escaping muons

We can see the muons that escape the vacuum chamber by looking at the ghost detector Hits from Renee’s detector (has art input tag of artg4:GhostCylinderDetector). We can also look at hits from my ghost detector that sits just inside the world cube.

Load the data

Tell PyRoot to load the necessary dictionaries.

readGalleryPy$provide_get_valid_handle('std::vector<gm2truth::GhostDetectorArtRecord>')
gh_cyl_tag <- readGalleryPy$createInputTag('artg4:GhostCylinderDetector')
gh_nwd_tag <- readGalleryPy$createInputTag('artg4:GhostNearWorldDetector')

Let’s try to load data!

pyClass = '
class GhostDetectorArtRecordReader:
  """Reader object to read Ghost Detector Art Record objects"""

  def __init__(self, inputTag):
    self.vals = []
    self.inputTag = inputTag
    self.getValidHandle = None
    self.names = ["fileEntry", "eventEntry", "particleID", "trackID", "parentTrackID", "x", "y", "z", 
                "px", "py", "pz"]

  def colnames(self):
    return self.names

  def prepare(self, ROOT, ev):
    self.vals = []  # Protect againt re-run
    self.getValidHandle = ev.getValidHandle(ROOT.vector(ROOT.gm2truth.GhostDetectorArtRecord))

  def fill(self, ROOT, ev):
    gh_cyl_h = self.getValidHandle(self.inputTag)  # Get the valid handle
    if not gh_cyl_h.empty():                       # Does it have data?
      gh_cyl = gh_cyl_h.product()                  # Get the corresponding data product vector
      for g in gh_cyl:                             # Loop over elements and fill
        if g.trackID == 1 and g.parentTrackID == 0:
          self.vals.append(
            [ev.fileEntry(), ev.eventEntry(), g.particleID, g.trackID, g.parentTrackID, 
            g.position.x(), g.position.y(), g.position.z(), 
            g.momentum.x(), g.momentum.y(), g.momentum.z()
            ])  
    return True
'

py_run_string(pyClass)
ghCReader <- main$GhostDetectorArtRecordReader(gh_cyl_tag)
ghNReader <- main$GhostDetectorArtRecordReader(gh_nwd_tag)
readers   <- readGalleryPy$GalleryReaders(list(ghCReader, ghNReader))

Read the data

readGalleryPy$getGalleryData(fileVec1, readers) -> times

Check out the time

times$allTime
ghcdf <- as.data.frame(do.call(rbind, ghCReader$vals))
ghcdf[] <- lapply(ghcdf, unlist)
names(ghcdf) <- ghCReader$colnames()
ghcdf
format(nrow(ghcdf), big.mark = ',')
ghndf <- as.data.frame(do.call(rbind, ghNReader$vals))
ghndf[] <- lapply(ghndf, unlist)
names(ghndf) <- ghNReader$colnames()
ghndf
ghCReader$colnames()
LS0tCnRpdGxlOiAiRXNjYXBlZCBNdW9ucyIKYXV0aG9yOiAiQWRhbSBMeW9uIgpkYXRlOiAyMDE3LTAzLTEwCm91dHB1dDogaHRtbF9ub3RlYm9vawotLS0KCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQprbml0cjo6b3B0c19jaHVuayRzZXQoY2FjaGU9VFJVRSwgYXV0b2RlcD1UUlVFKQpgYGAKCiMgSW50cm9kdWN0aW9uClBhcnQgb2YgdGhlIEFjY2VsZXJhdG9yIFJlYWRpbmVzcyBSZXZpZXcgaXMgbG9va2luZyBhdCBtdW9ucyB0aGF0IGxlYXZlIHRoZSByaW5nLiBUbyB0aGlzIGVuZCwgd2UgKFJlbmVlLCBOYXRoYW4sIEphbWVzLCBhbmQgbXlzZWxmKSBoYXZlIGEgcHJlbGltaW5hcnkgdmVyc2lvbiBvZiBNREMtMiB3aXRoIGZ1bGwgaW5qZWN0aW9uLCB1bmlmaWVkIGZpZWxkcywgYW5kICJnaG9zdCIgZGV0ZWN0b3JzIGluIGEgR2VhbnQgcGFyYWxsZWwgd29ybGQgKHNvIHRoZXNlIHNlbnNpdGl2ZSB2b2x1bWVzIGNhbiBiZSBwbGFjZWQgd2l0aG91dCBvdmVybGFwcGluZyBwaHlzaWNhbCBzdHJ1Y3R1cmVzKS4gV2UgY2FuIHJlbW92ZSB2YXJpb3VzIG1hZ25ldHMgYW5kIGZpZWxkcyBhbmQgc2VlIGhvdyB0aGF0IGFmZmVjdHMgbXVvbnMgZXNjYXBpbmcgdGhlIGJlYW0uIAoKCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CiMgTG9hZCBsaWJyYXJpZXMKbGlicmFyeShyZWFkR2FsbGVyeSkKbGlicmFyeShzdHJpbmdyKQpsaWJyYXJ5KGRwbHlyKQpsaWJyYXJ5KGdncGxvdDIpCmxpYnJhcnkocmdsKQpsaWJyYXJ5KHJlYWRyKQpsaWJyYXJ5KHB1cnJyKQpsaWJyYXJ5KHBhcmFsbGVsKQpgYGAKCgojIFNhbXBsZXMKSSBoYXZlIHJ1biBwcmVsaW1pbmFyeSBNREMtMiAodXNpbmcgUmVuZWUncyBGQ0wgZmlsZXMpLCBtYWtpbmcgMTBLIGV2ZW50cyBmb3IgdmFyaW91cyBzY2VuYXJpb3MuIEZpbGVzIGFyZSBjdXJyZW50bHkgc3RvcmVkIGluIGAvcG5mcy9HTTIvc2NyYXRjaC91c2Vycy9seW9uL2Fycl8yMDE3MDMxM2AgYW5kIGRpcmVjdG9yaWVzIHdpdGhpbi4gR2hvc3QgZGV0ZWN0b3JzIGluY2x1ZGVkIGFyZSBSZW5lZSdzIGN5bGluZGVyIGp1c3QgYXQgdGhlIG91dGVyIHZhY3V1bSB3YWxsIGFuZCBlbmNvbXBvc3NpbmcgdGhlIGluZmxlY3RvciBhcyB3ZWxsIGFzIG15IGdob3N0IGRldGVjdG9yIHRoYXQgaXMgYSBjeWxpbmRyaWNhbCBzaGVsbCBqdXN0IG9uIHRoZSBpbnNpZGUgb2YgdGhlIHdvcmxkIGN1YmUuIE5vdGUgdGhhdCB0aGlzIGNvZGUgaW5jbHVkZXMgdGhlIG1hZ25ldCB5b2tlIHN0ZWVsLiAKCiMjIEZpbmQgdGhlIGRhdGEKYGBge3J9CiMgTGV0J3MgbG9vayBhdCBvbmx5IHRoZSAxODAqIGpvYiBzZXJpZXMgKHNvIHdlIGp1c3QgZ2V0IDEwLDAwMCBldmVudHMgcGVyIHNjZW5hcmlvKQpzeXN0ZW0oJ2lmZGggbHMgL3BuZnMvR00yL3NjcmF0Y2gvdXNlcnMvbHlvbi9hcnJfMjAxNzAzMTMvMTgwKi8qLnJvb3QgfCBncmVwIC5yb290JywgaW50ZXJuPVQpICU+JSAKICB4cm9vdGlmeSgpIC0+IGFyckZpbGVzCmFyckZpbGVzCmBgYApMZXQncyBwbHVjayBvdXQgdGhlIHNjZW5hcmlvLgpgYGB7cn0Kc2NlbmFyaW9zIDwtIHN0cl9tYXRjaChhcnJGaWxlcywgJ2Fycl8yMDE3MDMxMy8uK191bmlmaWVkXyguKylfY3lsJylbLDJdCnNjZW5hcmlvcwpgYGAKCldoYXQgZGF0YSBhcmUgaW4gdGhlc2UgZmlsZT8gV2VsbCwgYHByb2R1Y3Rfc2l6ZXNfZHVtcGVyYCBkb2Vzbid0IHNlZW0gdG8gd29yayBhY3Jvc3MgWFJvb3RELiBUaGF0J3MgdG9vIGJhZC4gCgpIZXJlJ3MgYSBwaWN0dXJlIGZyb20gUm9vdC4KCiFbXShlc2NhcGVkTXVvbnNEYXRhLnBuZykKCiMjIExvb2sgYXQgVHJhY2tpbmcgYWN0aW9uIGRhdGEgdG8gc2VlIGhvdyBtbmF5IG11b25zIGRlY2F5IGFuZCBob3cgbWFueSBlc2NhcGUKCmBgYHtyfQp1c2VEYXRhUHJvZHVjdCgnc3RkOjp2ZWN0b3I8Z20ydHJ1dGg6OlRyYWNraW5nQWN0aW9uQXJ0UmVjb3JkPicpCmBgYAoKQ3JlYXRlIGEgcmVhZGVyIGNsYXNzIChtb2RpZnkgb3V0c2lkZSBvZiB0aGlzIG5vdGVib29rKSBmb3IgVHJhY2tpbmdBY3Rpb25BcnRSZWNvcmQKYGBgcgpyZWFkZXJDbGFzc1NrZWwoJ2dtMnRydXRoOjpUcmFja2luZ0FjdGlvbkFydFJlY29yZCcsIHdyaXRlRmlsZSA9ICd0cmFja2luZ0FjdGlvblJlYWRlci5weScpCmBgYAoKIyMjIGB0cmFja2luZ0FjdGlvblJlYWRlcmAKCkZvciBkb2N1bWVudGF0aW9uLCBoZXJlIGlzIHRoZSBgVHJhY2tpbmdBY3Rpb25BcnRSZWNvcmRSZWFkZXJgIHJlYWRlciBjbGFzcwpgYGB7cn0KcmVhZHI6OnJlYWRfZmlsZSgndHJhY2tpbmdBY3Rpb25SZWFkZXIucHknKSAlPiUgY2F0CmBgYAoKTWFrZSBhbiBpbnN0YW5jZQpgYGB7cn0KdHJhY2tpbmdBY3Rpb25SZWFkZXJDbGFzcyA8LSBjcmVhdGVSZWFkZXJDbGFzc19mcm9tX2ZpbGUoJ3RyYWNraW5nQWN0aW9uUmVhZGVyLnB5JykkVHJhY2tpbmdBY3Rpb25BcnRSZWNvcmRSZWFkZXIKYGBgCgpMb2FkIHRoZSBkYXRhIC0gbGV0J3MganVzdCBsb29rIGF0IG9uZSBmaWxlIHNpbmNlIGl0IHRha2VzIGEgbG9uZyB0aW1lCmBgYHtyfQp0YVJlYWRlciA8LSB0cmFja2luZ0FjdGlvblJlYWRlckNsYXNzKGFydElucHV0VGFnKCJhcnRnNCIpLCAyMDAwMCkKZ2V0R2FsbGVyeURhdGEoYXJyRmlsZXNbMV0sIHRhUmVhZGVyKQpgYGAKQXMgb2YgcmlnaHQgbm93LCB0aGUgZENhY2hlIFhSb290RCBkb29yIGlzIGRvbmUuIApgYGB7cn0KdGFERiA8LSBnYWxsZXJ5UmVhZGVyX2RmKHRyYWNraW5nQWN0aW9uUmVhZGVyKQpucm93KHRhREYpCmBgYApTZXQgdGhlIHNjZW5hcmlvICh3ZSdsbCBuZWVkIHRoYXQgYWdhaW4sIHNvIG1ha2UgaXQgYSBmdW5jdGlvbikKYGBge3J9CnNldFNjZW5hcmlvIDwtIGZ1bmN0aW9uKGRmKSB7CiAgZGYgJT4lIG11dGF0ZShzY2VuYXJpbyA9IGZhY3RvcihmaWxlRW50cnksIGxldmVscz0wOihsZW5ndGgoc2NlbmFyaW9zKS0xKSwgbGFiZWxzPXNjZW5hcmlvcykpCn0KYGBgCgpgYGB7cn0KdGFERiAlPiUgc2V0U2NlbmFyaW8oKSAlPiUgc2VsZWN0KHNjZW5hcmlvLCBldmVyeXRoaW5nKCkpIC0+IHRhREYKdGFERgpgYGAKCkhvdyBtYW55IHRyYWNrIGFjdGlvbiBoaXRzIGRvIHdlIGdldCBwZXIgc2NlbmFyaW8uCmBgYHtyfQp0YURGICU+JSBncm91cF9ieShzY2VuYXJpbykgJT4lIHRhbGx5KCkKYGBgCgpMZXQncyBqdXN0IGdldCB0aGUgZGVhdGggb2YgdGhlIG11b25zLiAKCiMjIyBNdW9uIGRlYXRocyBpbiB0cmFja2luZyBhY3Rpb24KCmBgYHtyfQp0YURGICU+JSBmaWx0ZXIoc3RhdHVzID09IDEpIC0+IHRhRGVhdGhERgpgYGAKCldlIG5lZWQgdGhlIHZvbHVtZSBJRC4gSSBoYXZlIGEgYXJ0IEZDTCB0byBydW4gdG8gZ2V0IHRoaXMgaW5mb3JtYXRpb24uIEkgaGF2ZSBgciBjYXQobGVuZ3RoKGFyckZpbGVzKSlgIGZpbGVzIGFuZCB0aGV5IGVhY2ggbWF5IGhhdmUgYSBkaWZmZXJlbnQgc2V0IG9mIHZvbHVtZSBJRHMuIExldCdzIHRyeSB0byBkbyB0aGlzIGluIHBhcmFsbGVsLiBUaGUgZXhhbXBsZXMgaW4gdGhlIGhlbHAgZm9yIGBwYXJhbGxlbDo6bWNwYXJhbGxlbGAgc2VlbSB0byBiZSB1c2VmdWwgaGVyZS4gCgpDcmVhdGUgdGhlIGNvbW1hbmQgc3RyaW5ncywKYGBge3J9CnJ1bkZvclZvbElEU3RyaW5nIDwtIGZ1bmN0aW9uKGkpIHsKICBzdHJfaW50ZXJwKAogICAgIlBWU19DU1ZPVVQ9JHtjc3ZvdXR9X3ZvbE5hbWVzLmNzdiBnbTIgLWMgJHtmY2xQYXRofS9nbTJhbmFseXNlcy9mY2wvcGh5c2ljYWxWb2x1bWVTdG9yZVRvRmlsZS5mY2wgLW4gMSAke2FGaWxlfSIsCiAgICBsaXN0KCBjc3ZvdXQ9c2NlbmFyaW9zW2ldLCBmY2xQYXRoPVN5cy5nZXRlbnYoIk1SQl9CVUlMRERJUiIpLCBhRmlsZT1hcnJGaWxlc1tpXSkgCiAgKQp9CgpydW5Gb3JWb2xJRFN0cmluZ3MgPC0gc2FwcGx5KDE6bGVuZ3RoKGFyckZpbGVzKSwgcnVuRm9yVm9sSURTdHJpbmcpCnJ1bkZvclZvbElEU3RyaW5ncwpgYGAKCkxldCdzIHJ1biBpbiBwYXJhbGxlbApgYGB7cn0Kam9icyA8LSBsYXBwbHkoMTpsZW5ndGgoYXJyRmlsZXMpLCBmdW5jdGlvbihpKSBtY3BhcmFsbGVsKHN5c3RlbSggcnVuRm9yVm9sSURTdHJpbmdzW2ldLCBpbnRlcm49VCApLCBuYW1lPWkpKQpyZXMgPC0gbWNjb2xsZWN0KGpvYnMpCmBgYAoKTm93IHdlIG5lZWQgdG8gbG9hZCB0aGUgY3N2IGZpbGVzLiBMZXQncyBkbyB0aGF0IGluIHBhcmFsbGVsIHRvbwpgYGB7ciwgbWVzc2FnZT1GQUxTRX0Kam9icyA8LSBsYXBwbHkoMTpsZW5ndGgoYXJyRmlsZXMpLCBmdW5jdGlvbihpKSBwYXN0ZTAoc2NlbmFyaW9zW2ldLCAiX3ZvbE5hbWVzLmNzdiIpICU+JSByZWFkX2Nzdihjb2xfbmFtZXM9Yygidm9sdW1lVUlEIiwgInZvbE5hbWUiKSkgJT4lIG1jcGFyYWxsZWwobmFtZT1pKSApCnZvbE5hbWVUYWJsZXMgPC0gbWNjb2xsZWN0KGpvYnMpCm5hbWVzKHZvbE5hbWVUYWJsZXMpIDwtIHNjZW5hcmlvcwpgYGAKCkxldCdzIHNlZSB3aGF0IHdlIGdvdApgYGB7cn0Kdm9sTmFtZVRhYmxlc1tbImV2ZXJ5dGhpbmciXV0KYGBgCkpvaW4gdGhlbSB0b2dldGhlciB3aGVuIHRoZSBzY2VuYXJpbyBuYW1lLgpgYGB7cn0Kdm9sTmFtZVRhYmxlIDwtIG1hcDJfZGYodm9sTmFtZVRhYmxlcywgc2NlbmFyaW9zLCBmdW5jdGlvbihkZiwgc2NlKSBkZiAlPiUgbXV0YXRlKHNjZW5hcmlvID0gc2NlKSApCnZvbE5hbWVUYWJsZQpgYGAKCk5vdyBsZXQncyBkbyBsb29rdXBzLi4uIFtOb3RlIHRoYXQgSSBjYW4ndCBkbyB3aGF0IEkgb3JpZ2luYWxseSB0aG91Z2h0LCB3aGljaCB3YXMgc3BsaXQgdXAgdGhlIGRhdGEgZnJhbWUgaW50byBncm91cHMgYnkgc2NlbmFyaW8gYW5kIHRoZW4gcHV0IGluIHRoZSBmYWN0b3IgYnkgZ3JvdXAgLS0gYmVjYXVzZSBpbiB0aGUgZW5kIHRoZSByb3dzIGdldCByZWpvaW5lZCBhbmQgdGhlbiByb3ctYmluZGVkIGZhY3RvciBnZXRzIG1lc3NlZCB1cC5dCgpgYGB7cn0KdGFEZWF0aERGIC0+IGhvbGQgIyBpbiBjYXNlIHdlIG1lc3MgdXAKdGFEZWF0aERGICU+JSBpbm5lcl9qb2luKHZvbE5hbWVUYWJsZSkgJT4lIHNlbGVjdChzY2VuYXJpbywgZXZlbnRFbnRyeSwgdm9sTmFtZSwgZXZlcnl0aGluZygpKSAtPiB0YURlYXRoREYKdGFEZWF0aERGCmBgYAoKV2UgY2FuIGNoZWNrIHRoaXMgLS0gd2Ugc2hvdWxkIHNlZSBkaWZmZXJlbnQgdm9sdW1lIG5hbWVzIGFzc29jaWF0ZWQgd2l0aCB2b2x1bWUgSURzIGZvciBkaWZmZXJlbnQgc2NlbmFyaW9zCmBgYHtyfQp0YURlYXRoREYgJT4lIGRpc3RpbmN0KHNjZW5hcmlvLCB2b2x1bWVVSUQsIHZvbE5hbWUpICU+JSBhcnJhbmdlKHZvbHVtZVVJRCkKYGBgCgpMZXQncyBwbG90IHdoZXJlIHRoaW5ncyBkaWUKCmBgYHtyfQp0YURlYXRoREYgJT4lIGdyb3VwX2J5KHNjZW5hcmlvLCB2b2xOYW1lKSAlPiUgdGFsbHkoKSAlPiUgYXJyYW5nZShzY2VuYXJpbywgZGVzYyhuKSkgLT4gZGVhdGhWb2x1bWVUYWJsZQpkZWF0aFZvbHVtZVRhYmxlCmBgYAoKRGlkIGFueSBvZiB0aGVtIGFjdHVhbGx5IGRlY2F5PwpgYGB7cn0KdGFEZWF0aERGICU+JSBmaWx0ZXIodm9sTmFtZSA9PSAiQXJjU2VjdGlvblswMF0iKQpgYGAKVWggb2ghISEhIEhhcmRseSBhbnkgZGVjYXkhCgpgYGB7cn0KdGFEZWF0aERGICU+JSBnZ3Bsb3QoIGFlcyh4ID0gdm9sTmFtZSkgKSArIGdlb21fYmFyKCkgKyB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZT05MCwgaGp1c3Q9MSkpCmBgYAoKIyMgTG9vayBhdCB3aHkgaGFyZGx5IGFueSBtdW9ucyBkZWNheS4gCgpMZXQncyBsb29rIGF0IHRoZSAnZXZlcnl0aGluZycgc2NlbmFyaW8uIApgYGB7cn0KZXZlcnl0aGluZ0ZpbGUgPC0gd2hpY2goc2NlbmFyaW9zID09ICJldmVyeXRoaW5nIikKYGBgCgpJIG1hZGUgYSBuZXcgVHJhY2tpbmdBY3Rpb24gcmVhZGVyCgpgYGB7cn0KcmVhZHI6OnJlYWRfZmlsZSgndHJhY2tpbmdBY3Rpb25SZWFkZXJDaGVja0RlY2F5LnB5JykgJT4lIGNhdApgYGAKTWFrZSBhbiBpbnN0YW5jZQpgYGB7cn0KdGFEZWNheUNsYXNzIDwtIGNyZWF0ZVJlYWRlckNsYXNzX2Zyb21fZmlsZSgndHJhY2tpbmdBY3Rpb25SZWFkZXJDaGVja0RlY2F5LnB5JykkVHJhY2tpbmdBY3Rpb25XaXRoRGVjYXlSZWFkZXIKYGBgCgpMb2FkIHRoZSBkYXRhIC0gbGV0J3MganVzdCBsb29rIGF0IG9uZSBmaWxlIHNpbmNlIGl0IHRha2VzIGEgbG9uZyB0aW1lCmBgYHtyfQp0YVJlYWRlciA8LSB0YURlY2F5Q2xhc3MoYXJ0SW5wdXRUYWcoImFydGc0IiksIDIwMDAwKQpnZXRHYWxsZXJ5RGF0YShhcnJGaWxlc1s1XSwgdGFSZWFkZXIpCmBgYApgYGB7cn0KdGFkREYgPC0gZ2FsbGVyeVJlYWRlcl9kZih0YVJlYWRlcikKbnJvdyh0YWRERikKYGBgCk1ha2UgdGhpbmdzIGVhc2llciB0byBsb29rIGF0CmBgYHtyfQp0YWRERiAlPiUgbXV0YXRlKHIgPSBzcXJ0KHgqeCt5KnkpKSAlPiUgc2VsZWN0KHRyYWNrVHlwZSwgdm9sdW1lVUlELCBzdGF0dXMsIHIsIGUsIHR1cm4pIC0+IHRhZERGCmBgYAoKU28gSSBzZWUgYW4gZWxlY3Ryb24gaW4gYWxvbXN0IGV2ZXJ5IGNhc2UuIEJ1dCB0aGUgbXVvbiBhcHBlYXJzIHRvIHN0b3AgaW4gdGhlIGlyb24uIExldCdzIGp1c3QgcGxvdCB4LHoKCmBgYHtyfQp0YURlYXRoREYgJT4lIGdncGxvdChhZXMoeD16LCB5PXgpKSArIAogIGdlb21fcG9pbnQoKSArIHhsaW0oYygtMTAwMDAsIDEwMDAwKSkKYGBgCmBgYHtyfQp0bXAgPC0gb3BlbjNkKCkKYGBgCmBgYHtyfQpyaW5nIDwtIGN5bGluZGVyM2QoIGNlbnRlcj1yYmluZChjKDAsLTkwLCAwKSwgYygwLDkwLDApKSwKICAgICAgICAgICAgICAgICAgICByYWRpdXM9NzExMiwKICAgICAgICAgICAgICAgICAgICBzaWRlcz0yMCwgY2xvc2VkID0gRikKYGBgCmBgYHtyfQpjbGVhcjNkKCkKd2l0aCh0YURlYXRoREYsIAogICAgIHBsb3QzZCh4PXgsIHk9eSwgej16LCB0eXBlPSdwJywgbWFpbj0nTXVvbiBkZWF0aCBwb3NpdGlvbnMnLCAKICAgICAgIHhsYWI9IngiLCB5bGFiPSJ5IiwgemxhYj0ieiIsIHhsaW09YygtOTAwMCwgOTAwMCksIHpsaW09YygtOTAwMCwgOTAwMCkpKQpwbG90M2QocmluZywgYWRkPVQsIGFscGhhPTAuMikKdmlldzNkKHBoaT05MCwgdGhldGE9LTkwKQpyZ2x3aWRnZXQoKQpgYGAKVGhpcyByZWFsbHkgZG9lc24ndCBsb29rIGxpa2UgbXVvbnMgYXJlIGJlaW5nIHN0b3JlZC4KCgoKIyMgTG9vayBhdCBSZW5lZSdzIGN5bGluZGVyIGhpdHMgKHRoaXMgaXMgdGhlIGN5bGluZGVyIGp1c3QgaW4gdGhlIG91dGVyIHdhbGwgb2YgdGhlIHZhY3V1bSBjaGFtYmVyIGFuZCBhcm91bmQgdGhlIGluZmxlY3RvcikuCmBgYHtyfQp1c2VEYXRhUHJvZHVjdCgnc3RkOjp2ZWN0b3I8Z20ydHJ1dGg6Okdob3N0RGV0ZWN0b3JBcnRSZWNvcmQ+JykKZ2hvc3RDeWxpbmRlcklucHV0VGFnID0gYXJ0SW5wdXRUYWcoJ2FydGc0Okdob3N0Q3lsaW5kZXJEZXRlY3RvcicpCmBgYAoKQ3JlYXRlIGEgcmVhZGVyIGNsYXNzIChtb2RpZnkgb3V0c2lkZSBvZiB0aGlzIG5vdGVib29rKSBmb3IgR2hvc3REZXRlY3RvckFydFJlY29yZApgYGByCnJlYWRlckNsYXNzU2tlbCgnZ20ydHJ1dGg6Okdob3N0RGV0ZWN0b3JBcnRSZWNvcmQnLCB3cml0ZUZpbGUgPSAnZ2hvc3REZXRlY3RvclJlYWRlci5weScpCmBgYApIZXJlJ3MgdGhlIHJlYWRlciBjbGFzcwpgYGB7cn0KcmVhZHI6OnJlYWRfZmlsZSgnZ2hvc3REZXRlY3RvclJlYWRlci5weScpICU+JSBjYXQKYGBgCgpNYWtlIGFuIGluc3RhbmNlCmBgYHtyfQpnaG9zdERldGVjdG9yUmVhZGVyQ2xhc3MgPC0gY3JlYXRlUmVhZGVyQ2xhc3NfZnJvbV9maWxlKCdnaG9zdERldGVjdG9yUmVhZGVyLnB5JykkR2hvc3REZXRlY3RvckFydFJlY29yZFJlYWRlcgpnaG9zdERldGVjdG9yUmVhZGVyID0gZ2hvc3REZXRlY3RvclJlYWRlckNsYXNzKGdob3N0Q3lsaW5kZXJJbnB1dFRhZykKYGBgCgpMb2FkIHRoZSBkYXRhCmBgYHtyfQpnZXRHYWxsZXJ5RGF0YShhcnJGaWxlcywgZ2hvc3REZXRlY3RvclJlYWRlcikKYGBgCmBgYHtyfQpnY0RGIDwtIGdhbGxlcnlSZWFkZXJfZGYoZ2hvc3REZXRlY3RvclJlYWRlcikKYGBgCgpIb3cgbWFueSBldmVudHMgZGlkIHdlIGdldD8KYGBge3J9Cm5yb3coZ2NERikKYGBgCmBgYHtyfQpoZWFkKGdjREYpCmBgYAoKTWFrZSBmYWN0b3JzLi4uCgpTb21lIGNvbW1vbiBwYXJ0aWNsZXMgYXJlCmBgYHtyfQojIFNlZSAgaHR0cDovL3BkZy5sYmwuZ292LzIwMDcvcmV2aWV3cy9tb250ZWNhcmxvcnBwLnBkZiBmb3IgUERHIHZhbHVlIG1lYW5pbmdzCnBkZ3MgPC0gYygnZS0nPSAxMSwgJ251X2UnPSAxMiwgJ211LSc9IDEzLCAnbnVfbXUnPSAxNCwgJ3RhdS0nPSAxNSwgJ251X3RhdSc9IDE2LAogICAgICAgICAgJ2UrJz0tMTEsICdhbnRpX251X2UnPS0xMiwgJ211Kyc9LTEzLCAnYW50aV9udV9tdSc9LTE0LCAndGF1Kyc9LTE1LCAnYW50aV9udV90YXUnPS0xNiwKICAgICAgICAgICdnYW0nPTIyLCAncCc9MjIxMiwgJ24nPTIxMTIsICdhbnRpLXAnPS0yMjEyLCAnYW50aS1uJz0tMjExMikgCnBkZ3MKYGBgCgoKCmBgYHtyfQpnY0RGICU+JSBtdXRhdGUoc2NlbmFyaW8gPSBmYWN0b3IoZmlsZUVudHJ5LCBsZXZlbHM9MDoobGVuZ3RoKHNjZW5hcmlvcyktMSksIGxhYmVscz1zY2VuYXJpb3MpLAogICAgICAgICAgICAgICAgcGRnID0gZmFjdG9yKHBhcnRpY2xlSUQsIGxldmVscz1wZGdzLCBsYWJlbHM9bmFtZXMocGRncykpKSAlPiUgCiAgc2VsZWN0KHNjZW5hcmlvLCBldmVudEVudHJ5LCBwZGcsIGV2ZXJ5dGhpbmcoKSktPiBnY0RGCmdjREYKYGBgCgpIb3cgbWFueSBsb3N0IHBlciBzY2VuYXJpbwpgYGB7cn0KZ2NERiAlPiUgZmlsdGVyKHBkZyA9PSAnbXUrJykgJT4lIGdyb3VwX2J5KHNjZW5hcmlvKSAlPiUgdGFsbHkoKQpgYGAKSG93IG1hbnkgZXZlbnRzPwpgYGB7cn0KZ2NERiAlPiUgZ3JvdXBfYnkoc2NlbmFyaW8pICU+JSB0YWxseSgpCmBgYAoKCiMgU2VlIGV2aWRlbmNlIG9mIGVzY2FwaW5nIG11b25zCgpXZSBjYW4gc2VlIHRoZSBtdW9ucyB0aGF0IGVzY2FwZSB0aGUgdmFjdXVtIGNoYW1iZXIgYnkgbG9va2luZyBhdCB0aGUgZ2hvc3QgZGV0ZWN0b3IgSGl0cyBmcm9tIFJlbmVlJ3MgZGV0ZWN0b3IgKGhhcyBhcnQgaW5wdXQgdGFnIG9mIGBhcnRnNDpHaG9zdEN5bGluZGVyRGV0ZWN0b3JgKS4gV2UgY2FuIGFsc28gbG9vayBhdCBoaXRzIGZyb20gbXkgZ2hvc3QgZGV0ZWN0b3IgdGhhdCBzaXRzIGp1c3QgaW5zaWRlIHRoZSB3b3JsZCBjdWJlLiAKCiMjIExvYWQgdGhlIGRhdGEKClRlbGwgUHlSb290IHRvIGxvYWQgdGhlIG5lY2Vzc2FyeSBkaWN0aW9uYXJpZXMuIApgYGB7cn0KcmVhZEdhbGxlcnlQeSRwcm92aWRlX2dldF92YWxpZF9oYW5kbGUoJ3N0ZDo6dmVjdG9yPGdtMnRydXRoOjpHaG9zdERldGVjdG9yQXJ0UmVjb3JkPicpCmBgYAoKYGBge3J9CmdoX2N5bF90YWcgPC0gcmVhZEdhbGxlcnlQeSRjcmVhdGVJbnB1dFRhZygnYXJ0ZzQ6R2hvc3RDeWxpbmRlckRldGVjdG9yJykKZ2hfbndkX3RhZyA8LSByZWFkR2FsbGVyeVB5JGNyZWF0ZUlucHV0VGFnKCdhcnRnNDpHaG9zdE5lYXJXb3JsZERldGVjdG9yJykKYGBgCgpMZXQncyB0cnkgdG8gbG9hZCBkYXRhIQpgYGB7cn0KcHlDbGFzcyA9ICcKY2xhc3MgR2hvc3REZXRlY3RvckFydFJlY29yZFJlYWRlcjoKICAiIiJSZWFkZXIgb2JqZWN0IHRvIHJlYWQgR2hvc3QgRGV0ZWN0b3IgQXJ0IFJlY29yZCBvYmplY3RzIiIiCgogIGRlZiBfX2luaXRfXyhzZWxmLCBpbnB1dFRhZyk6CiAgICBzZWxmLnZhbHMgPSBbXQogICAgc2VsZi5pbnB1dFRhZyA9IGlucHV0VGFnCiAgICBzZWxmLmdldFZhbGlkSGFuZGxlID0gTm9uZQogICAgc2VsZi5uYW1lcyA9IFsiZmlsZUVudHJ5IiwgImV2ZW50RW50cnkiLCAicGFydGljbGVJRCIsICJ0cmFja0lEIiwgInBhcmVudFRyYWNrSUQiLCAieCIsICJ5IiwgInoiLCAKICAgICAgICAgICAgICAgICJweCIsICJweSIsICJweiJdCgogIGRlZiBjb2xuYW1lcyhzZWxmKToKICAgIHJldHVybiBzZWxmLm5hbWVzCgogIGRlZiBwcmVwYXJlKHNlbGYsIFJPT1QsIGV2KToKICAgIHNlbGYudmFscyA9IFtdICAjIFByb3RlY3QgYWdhaW50IHJlLXJ1bgogICAgc2VsZi5nZXRWYWxpZEhhbmRsZSA9IGV2LmdldFZhbGlkSGFuZGxlKFJPT1QudmVjdG9yKFJPT1QuZ20ydHJ1dGguR2hvc3REZXRlY3RvckFydFJlY29yZCkpCgogIGRlZiBmaWxsKHNlbGYsIFJPT1QsIGV2KToKICAgIGdoX2N5bF9oID0gc2VsZi5nZXRWYWxpZEhhbmRsZShzZWxmLmlucHV0VGFnKSAgIyBHZXQgdGhlIHZhbGlkIGhhbmRsZQogICAgaWYgbm90IGdoX2N5bF9oLmVtcHR5KCk6ICAgICAgICAgICAgICAgICAgICAgICAjIERvZXMgaXQgaGF2ZSBkYXRhPwogICAgICBnaF9jeWwgPSBnaF9jeWxfaC5wcm9kdWN0KCkgICAgICAgICAgICAgICAgICAjIEdldCB0aGUgY29ycmVzcG9uZGluZyBkYXRhIHByb2R1Y3QgdmVjdG9yCiAgICAgIGZvciBnIGluIGdoX2N5bDogICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgTG9vcCBvdmVyIGVsZW1lbnRzIGFuZCBmaWxsCiAgICAgICAgaWYgZy50cmFja0lEID09IDEgYW5kIGcucGFyZW50VHJhY2tJRCA9PSAwOgogICAgICAgICAgc2VsZi52YWxzLmFwcGVuZCgKICAgICAgICAgICAgW2V2LmZpbGVFbnRyeSgpLCBldi5ldmVudEVudHJ5KCksIGcucGFydGljbGVJRCwgZy50cmFja0lELCBnLnBhcmVudFRyYWNrSUQsIAogICAgICAgICAgICBnLnBvc2l0aW9uLngoKSwgZy5wb3NpdGlvbi55KCksIGcucG9zaXRpb24ueigpLCAKICAgICAgICAgICAgZy5tb21lbnR1bS54KCksIGcubW9tZW50dW0ueSgpLCBnLm1vbWVudHVtLnooKQogICAgICAgICAgICBdKSAgCiAgICByZXR1cm4gVHJ1ZQonCgpweV9ydW5fc3RyaW5nKHB5Q2xhc3MpCmBgYAoKYGBge3J9CmdoQ1JlYWRlciA8LSBtYWluJEdob3N0RGV0ZWN0b3JBcnRSZWNvcmRSZWFkZXIoZ2hfY3lsX3RhZykKZ2hOUmVhZGVyIDwtIG1haW4kR2hvc3REZXRlY3RvckFydFJlY29yZFJlYWRlcihnaF9ud2RfdGFnKQpyZWFkZXJzICAgPC0gcmVhZEdhbGxlcnlQeSRHYWxsZXJ5UmVhZGVycyhsaXN0KGdoQ1JlYWRlciwgZ2hOUmVhZGVyKSkKYGBgCgpSZWFkIHRoZSBkYXRhCmBgYHtyfQpyZWFkR2FsbGVyeVB5JGdldEdhbGxlcnlEYXRhKGZpbGVWZWMxLCByZWFkZXJzKSAtPiB0aW1lcwpgYGAKCkNoZWNrIG91dCB0aGUgdGltZQpgYGB7cn0KdGltZXMkYWxsVGltZQpgYGAKCmBgYHtyfQpnaGNkZiA8LSBhcy5kYXRhLmZyYW1lKGRvLmNhbGwocmJpbmQsIGdoQ1JlYWRlciR2YWxzKSkKZ2hjZGZbXSA8LSBsYXBwbHkoZ2hjZGYsIHVubGlzdCkKbmFtZXMoZ2hjZGYpIDwtIGdoQ1JlYWRlciRjb2xuYW1lcygpCmdoY2RmCmBgYAoKCmBgYHtyfQpmb3JtYXQobnJvdyhnaGNkZiksIGJpZy5tYXJrID0gJywnKQpgYGAKCgpgYGB7cn0KZ2huZGYgPC0gYXMuZGF0YS5mcmFtZShkby5jYWxsKHJiaW5kLCBnaE5SZWFkZXIkdmFscykpCmdobmRmW10gPC0gbGFwcGx5KGdobmRmLCB1bmxpc3QpCm5hbWVzKGdobmRmKSA8LSBnaE5SZWFkZXIkY29sbmFtZXMoKQpnaG5kZgpgYGAKCmBgYHtyfQpnaENSZWFkZXIkY29sbmFtZXMoKQpgYGAKCg==